package com.elf.bluetoothtest.bluetooth;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.elf.bluetoothtest.utils.ByteUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class BleBluetoothConnection implements IBlueToothConnection {

    private final static String ERROR_NO_CONNECT = "请先连接蓝牙设备";
    private final static String ERROR_NO_FOUND_SERVICE = "未找到指定的服务";
    private final static String ERROR_NO_FOUND_CHARACTERISTIC = "未找到指定的特征值";
    private final static String ERROR_NO_FOUND_DESCRIPTOR = "未找到指定的描述";
    private final static String ERROR_CHARACTERISTIC_NOT_NOTIFY = "该特征值不可监听";

    private static final int WRITE_DATA_BUFFER_SIZE = 20;

    private final static String DESCRIPTOR_ID = "00002902-0000-10008000-00805f9b34fb";

    private final Context mContext;
    private final BluetoothDevice mBluetoothDevice;
    private final BluetoothGattCallback mBluetoothGattCallback;
    private BluetoothGatt mBluetoothGatt;
    private BluetoothConnectionListener mBluetoothConnectionListener;
    private BLEOperationListener mBLEOperationListener;

    public BleBluetoothConnection(Context context, BluetoothDevice device) {
        mContext = context;
        mBluetoothDevice = device;
        mBluetoothGattCallback = new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    //连接成功
                    gatt.discoverServices();
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    //连接断开
                    if (mBluetoothConnectionListener != null) {
                        mBluetoothConnectionListener.onDisconnected(mBluetoothDevice);
                    }
                }
            }

            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    //发现服务成功
                    if (mBluetoothConnectionListener != null) {
                        mBluetoothConnectionListener.onConnected(mBluetoothDevice);
                    }
                }
            }

            @Override
            public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                //读取特征值回调
                if (mBLEOperationListener != null) {
                    mBLEOperationListener.onCharacteristicRead(characteristic, status);
                }
            }

            @Override
            public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                //写入特征值回调
                if (mBLEOperationListener != null) {
                    mBLEOperationListener.onCharacteristicWrite(characteristic, status);
                }
            }

            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                //特征值改变回调
                if (mBLEOperationListener != null) {
                    mBLEOperationListener.onCharacteristicChanged(characteristic);
                }
            }
        };
    }

    @Override
    public BluetoothDevice getDeviceInfo() {
        return mBluetoothDevice;
    }

    @Override
    public void connect() {
        mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false, mBluetoothGattCallback);
    }

    @Override
    public void disconnect() {
        if (mBluetoothGatt != null) {
            mBluetoothGatt.disconnect();
        }
    }

    @Override
    public void release() {
        try {
            disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取所有服务
     */
    public List<BluetoothGattService> getServices() {
        if (mBluetoothGatt == null) {
            return new ArrayList<>();
        }
        return mBluetoothGatt.getServices();
    }

    /**
     * 获取指定服务下的所有特征值
     *
     * @param serviceId 服务id
     */
    public List<BluetoothGattCharacteristic> getCharacteristics(String serviceId) {
        BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(serviceId));
        if (service == null) {
            return new ArrayList<>();
        }
        return service.getCharacteristics();
    }

    /**
     * 设置监听特征值改变
     *
     * @param serviceId        服务ID
     * @param characteristicId 特征值ID
     * @param descriptorId     描述ID
     * @param state            状态 true启用，false不启用
     */
    public boolean notifyCharacteristicValueChange(String serviceId, String characteristicId, String descriptorId, boolean state) throws Exception {
        if (mBluetoothGatt == null) {
            throw new Exception(ERROR_NO_CONNECT);
        }
        BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(serviceId));
        if (service == null) {
            throw new Exception(ERROR_NO_FOUND_SERVICE);
        }
        BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characteristicId));
        return notifyCharacteristicValueChange(characteristic, descriptorId, state);
    }

    public boolean notifyCharacteristicValueChange(BluetoothGattCharacteristic characteristic, String descriptorId, boolean state) throws Exception {
        if (characteristic == null) {
            throw new Exception(ERROR_NO_FOUND_CHARACTERISTIC);
        }
        if (!((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0)) {
            throw new Exception(ERROR_CHARACTERISTIC_NOT_NOTIFY);
        }

        if (mBluetoothGatt.setCharacteristicNotification(characteristic, state)) {
            if (!TextUtils.isEmpty(descriptorId)) {
                BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(descriptorId));
                if (descriptor == null) {
                    throw new Exception(ERROR_NO_FOUND_DESCRIPTOR);
                }
                boolean b = descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                if (!b) {
                    throw new Exception("设置描述值失败");
                }
                return mBluetoothGatt.writeDescriptor(descriptor);
            } else {
                List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
//                boolean result = false;
                for (BluetoothGattDescriptor descriptor : descriptors) {
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    mBluetoothGatt.writeDescriptor(descriptor);
//                    if (!result) {
//                        throw new Exception("设置特征值失败");
//                    }
                }
//                return result;
                return true;
            }
        } else {
            throw new Exception("设置特征值失败");
        }
    }

    /**
     * 读取特征值
     *
     * @param serviceId        服务ID
     * @param characteristicId 特征值ID
     */
    public boolean readCharacteristicValue(String serviceId, String characteristicId) throws Exception {
        if (mBluetoothGatt == null) {
            throw new Exception(ERROR_NO_CONNECT);
        }
        BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(serviceId));
        if (service == null) {
            throw new Exception(ERROR_NO_FOUND_SERVICE);
        }
        BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characteristicId));
        if (characteristic == null) {
            throw new Exception(ERROR_NO_FOUND_CHARACTERISTIC);
        }
        return mBluetoothGatt.readCharacteristic(characteristic);
    }

    /**
     * 写入特征值
     *
     * @param serviceId        服务ID
     * @param characteristicId 特征值ID
     * @param hexStr           16进制字符串
     */
    public boolean writeCharacteristicValue(String serviceId, String characteristicId, String hexStr) throws Exception {
        if (mBluetoothGatt == null) {
            throw new Exception(ERROR_NO_CONNECT);
        }
        BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(serviceId));
        if (service == null) {
            throw new Exception(ERROR_NO_FOUND_SERVICE);
        }
        BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characteristicId));
        if (characteristic == null) {
            throw new Exception(ERROR_NO_FOUND_CHARACTERISTIC);
        }
        characteristic.setValue(hexStr);
        return mBluetoothGatt.writeCharacteristic(characteristic);
    }

    public boolean writeCharacteristicValue(BluetoothGattCharacteristic characteristic, byte[] data, long time) {
        boolean ret = false;
        if (data == null) {
            return false;
        }
        int length = data.length;
        if (length <= WRITE_DATA_BUFFER_SIZE) {
            characteristic.setValue(data);
            ret = mBluetoothGatt.writeCharacteristic(characteristic);
        } else {
            int count = 0;
            int offset = 0;
            while (offset < length) {
                try {
                    Thread.sleep(time);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                if ((length - offset) < WRITE_DATA_BUFFER_SIZE) {
                    count = length - offset;
                } else {
                    count = WRITE_DATA_BUFFER_SIZE;
                }
                byte tempArray[] = new byte[count];
                System.arraycopy(data, offset, tempArray, 0, count);
                Log.i("ble", "write data: " + ByteUtil.bytesToHexString(tempArray));

                characteristic.setValue(tempArray);
                ret = mBluetoothGatt.writeCharacteristic(characteristic);
                Log.i("ble", "write data result: " + ret);
                if (!ret) {
                    return ret;
                }

                offset = offset + count;
            }
        }
        return ret;

    }

    @Override
    public void setBluetoothConnectionListener(BluetoothConnectionListener listener) {
        mBluetoothConnectionListener = listener;
    }

    public void setBLEOperationListener(BLEOperationListener listener) {
        mBLEOperationListener = listener;
    }

    public interface BLEOperationListener {
        void onCharacteristicRead(BluetoothGattCharacteristic characteristic, int status);

        void onCharacteristicChanged(BluetoothGattCharacteristic characteristic);

        void onCharacteristicWrite(BluetoothGattCharacteristic characteristic, int status);
    }
}
